
import network
import socket
import time
import uos
import json
from lonon_mqtt import  MQTTClient
import machine
from hmacSha256 import HMACSha256

# 连接mqtt服务器参数
# 
# 产品id+设备id = 用户id
# ClientID:RT0JAO4YEVesp32   
# 
# 产品id + 设备id + 固定 + 随机 + 时间
# 
# 服务器地址：RT0JAO4YEV.iotcloud.tencentdevices.com
# 
# 端口：1883
# 
# 登录信息
# 
# Username:RT0JAO4YEVesp32;12010126;79RTK;1633409727
# 
# Password:4a3c81aee1da0703fbf1e10506386ff887662d7880d8f4a5e0d10296100aded2;hmacsha256
# 
# Password方法：使用Username加密钥LONON-IOT-LED计算 hmacsha256 末尾加上“;hmacsha265”
# 
# SSID = "DESKTOP-U3NFQ55"  #修改为你的WiFi名称
# PASSWORD = "00000000"  #修改为你WiFi密码
# AP模式本地ip地址192.168.4.1
# 

SSID = "Lonon-F3"  #修改为你的WiFi名称
PASSWORD = "13630055772"  #修改为你WiFi密码
port = 10000  #  #wlan
listenSocket = None  #套接字
wlan = None
js_ssid_info = None #ssid json字符串存放
tencent_mqtt = None


# 设备信息类
class device_info:
    def __init__(self):
        self.humidity = 89
        self.light = 0
        self.light_intensity  = 9
        self.message  = 'hello class'
        self.motor  = 1
        self.temperature  = '12'

# 设备信息对象
dev = device_info()



# 获取影子数据
def get_shadowdata():

  dic2 = {}
  dic3 = {}
  dic4 = {}
  dic5 = {}

  dic3['humidity'] =  dev.humidity                    # 湿度
  dic3['light'] = dev.light                           # 照明
  dic3['light_intensity'] = dev.light_intensity       # 光照
  dic3['message'] = dev.message                       # 消息
  dic3['motor'] = dev.motor                           # 风扇
  dic3['temperature'] = dev.temperature               # 温度

  dic2['reported'] = dic3
  dic4['desired'] = None
  dic4['reported'] = dic3
  dic5['type'] = 'update'
  dic5['version'] = 0
  dic5['clientToken'] = 'clientToken'
  dic5['state'] = dic4

  print(json.dumps(dic5))
  return dic5


#连接WiFi
def connectWifi(ssid,passwd):   
  global wlan
  wlan = network.WLAN(network.STA_IF) 
  wlan.active(True)   #激活网络
  wlan.disconnect()   #断开WiFi连接
  wlan.connect(ssid, passwd)   #连ifconfig()[0] == '0.0.0.0'):   #等待连接
  time.sleep(1)
  return True


#开启udp服务  等待获取wiif连接参数 
def get_wifi_info():
  global js_ssid_info
  global listenSocket
  print('go get_wifi_info')
  ap = network.WLAN(network.AP_IF)     #创建接入点界面
  ap.active(True)                      #激活界面
  ap.config(essid='ESP-AP',channel=1)  #设置接入点的ESSID，和WiFi 通道

  while True:
    time.sleep(1)
    print('waiting connect...')
    ip = ap.ifconfig()[0]   #获取IP地址
    print(ip)
    if ap.isconnected() == 1:
      print('isconnected!')
      listenSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      listenSocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  
      listenSocket.bind((ip,port))  #绑定端口
      while True:              
        print('waiting data...')
        data,addr=listenSocket.recvfrom(1024)
        print('received:',data,'from',addr)
        listenSocket.sendto('esp32_ok',addr)
        try:
          js_ssid_info = json.loads(data)
          print(type(js_ssid_info), js_ssid_info)
          if js_ssid_info.get('ssid') != None and js_ssid_info.get('ps') != None:
            print('ssid info ok!')
            print('ssid = ' + js_ssid_info.get('ssid'))
            print('ps = ' + js_ssid_info.get('ps'))
            print(' %s' % ap.active())
            ap.active(False)  # 关闭ap
            listenSocket.close()  # 关闭udp
            return True
          else:
            print('ssid info error!')
        except:
          print('json error')
          ap.active(False)  # 关闭ap
          listenSocket.close() # 关闭udp
          return False

#检查是否存储了ssid信息
def chack_save_ssid_info():    
  global js_ssid_info
  try:
    f = open ( 'wifi_info.py', 'r')
    if  f != None:  # 有存储该文件
      ssid = f.read()
      print('ssid info = [' + ssid + ']')
      js_ssid_info = json.loads(ssid) # 解析为json对象
      if js_ssid_info.get('ssid') != None and js_ssid_info.get('ssid') != None: # 检查json  key
        print('ssid info data ok!')
        return True # ssid信息有效
      else:       
        print('ssid info data error!')
        f.close() 
        return False  # ssid信息无效
    else: # 没有该文件
      print('ssid file = None')
      return False
    return True
  except:
    print('chack_save_ssid_info = false!')
    return False



# mqtt 消息回调
def mqtt_callback(topic, msg):
  print('this is mqtt callback!')
  print('topic = ' + topic.decode()) # Python decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码。
  print('msg = ' + msg.decode()) # Python decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码。
  get_msg = json.loads(msg.decode()) # 消息转json对象

  # 设备数据对象展示
  # self.humidity = 89
  # self.light = 0
  # self.light_intensity  = 9
  # self.message  = 'hello class'
  # self.motor  = 1
  # self.temperature  = '12'

  if get_msg != None: # 解析json数据成功
    if get_msg.get('motor') != None: # 操作风扇
      dev.motor = get_msg.get('motor')
    if get_msg.get('light') != None: # 操作照明
      dev.light = get_msg.get('light')
    # 发布影子消息
    print('tencent_mqtt.publish ok')
    tencent_mqtt.publish('$shadow/operation/RT0JAO4YEV/esp32',json.dumps(get_shadowdata()))



  
# bytes转十六进制字符串
def bytesToHexString(bs: bytes) -> str:
    return ''.join(['%02x' % b for b in bs])


# 获取mqtt登录密码
def get_mqtt_connect_password(pd_id,dev_id):
  cient_id = pd_id + dev_id
  username = cient_id + ';12010126;79RTK;1633409727'
  key = b'LONON-IOT-LED'
  hmac_password = HMACSha256(key,username)
  mqtt_password = bytesToHexString(hmac_password) + ';hmacsha256'
  return mqtt_password


# mqtt 心跳定时回调
def heart_beat_timer_callback(timer):
  global tencent_mqtt
  tencent_mqtt.ping()
  

 # main
def main():
  #Catch exceptions,stop program if interrupted accidentally in the 'try'
  try_get_ssid_info_fig = False
  try:
    print('---------------------------------------------------- start ----------------------------------------------------')
    if chack_save_ssid_info() == True:  # 检查本地存储的ssid信息是否有效  如果有有效则ssid信息已经放入js_ssid_info
      pass
    else: # 本地ssid信息无效  执行 get_wifi_info 打开ap部署udp服务端等待接收ssid信息
      try_get_ssid_info_fig = True  # 标记为尝试获取ssid
      get_wifi_info()
      print('get_wifi_info = True')
      print('goto connectWifi')
      

    print(type(js_ssid_info), js_ssid_info)
    print('ssid = ' + js_ssid_info.get('ssid')) 
    print('ps = ' + js_ssid_info.get('ps'))
    connectWifi(js_ssid_info.get('ssid'),js_ssid_info.get('ps'))

    while True:
      if  wlan.isconnected() == True: # 连接成功
        print('connect ok!')
        if  try_get_ssid_info_fig == True:
          f = open('wifi_info.py', 'w')
          f.write(json.dumps(js_ssid_info))  # 保存ssid信息
          f.close()
          print('write ssid info ok!')
        break

    # 初始化mqtt登录相关字段
    pd_id = 'RT0JAO4YEV'
    dev_id = 'esp32'
    cient_id = pd_id + dev_id
    service_address = pd_id + '.iotcloud.tencentdevices.com'
    username = cient_id + ';12010126;79RTK;1633409727'
    
    # 初始化mqtt
    print('MQTTClient start')
    global tencent_mqtt
    tencent_mqtt = MQTTClient(cient_id,service_address,1883,username,get_mqtt_connect_password(pd_id,dev_id),60)
    print('MQTTClient ok')

    # 连接mqtt
    if tencent_mqtt.connect() == 0:
      print('tencent_mqtt.connect() = 0')
      timer = machine.Timer(0)
      timer.init(period = 6000, mode = machine.Timer.PERIODIC, callback = heart_beat_timer_callback)

      # 设置消息回调
      print('tencent_mqtt.set_callback ok')
      tencent_mqtt.set_callback(mqtt_callback)

      # 订阅消息
      print('tencent_mqtt.subscribe ok')
      tencent_mqtt.subscribe('RT0JAO4YEV/esp32/data')
       
      # 发布影子消息
      print('tencent_mqtt.publish ok')
      tencent_mqtt.publish('$shadow/operation/RT0JAO4YEV/esp32',json.dumps(get_shadowdata()))

      while True:
        tencent_mqtt.wait_msg()

    else:
      print('tencent_mqtt.connect() != 0')
  except Exception as e:  
      print ('Exception')
      print (e)
      if(listenSocket):   #判断套接字是否为空
          listenSocket.close()   #关闭套接字
      #wlan.disconnect()   #断开WiFi
      #wlan.active(False)   #冻结网络


main()
